package oop.ex2.commands;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import oop.ex2.exceptions.EmptyExpression;
import oop.ex2.exceptions.ExpectedAtomicExpression;
import oop.ex2.exceptions.ExpectedBinaryOperation;
import oop.ex2.exceptions.TerminatingStringNotFound;
import oop.ex2.exceptions.TokenNotRecognized;
import oop.ex2.exceptions.UnrecognizedVariable;
import oop.ex2.expression.ExpressionSolver;
import oop.ex2.main.OutputFormatter;
import oop.ex2.tokenizer.VariableAnalyzer;

/**
 * This is the base class for all of the commands that do assignments. It's in charge of
 * parsing the command and updating the variable using the expression. The base classes
 * only have to implement an operation that's preformed on the expression values before
 * the assignment.
 * @author daniel_i_l
 *
 */
public abstract class AssignmentCommand extends Command {
	private static final String MANDATORY_WHITESPACE = "\\s";
	private Pattern _assignmentOp;
	
	/**
	 * Create an assignment command using a specified ExpressionSolver and the assignment operation
	 * string that the command should look for after the variable name.
	 * @param expressionSolver The expression solver to use.
	 * @param assignmentOp The assignment operator to look for.
	 */
	public AssignmentCommand(ExpressionSolver expressionSolver, String assignmentOp) {
		super(expressionSolver);
		_assignmentOp = Pattern.compile(MANDATORY_WHITESPACE + assignmentOp + MANDATORY_WHITESPACE);
	}

	@Override
	public boolean executeCommand(String command) 
	throws TokenNotRecognized, EmptyExpression, ExpectedAtomicExpression, 
	ExpectedBinaryOperation, TerminatingStringNotFound, UnrecognizedVariable {
		
		//first check for a variable
		VariableAnalyzer varAnalyzer = new VariableAnalyzer(null);
		int endPos = varAnalyzer.getEndOfPatternPosition(command);
		
		if (endPos < 0)
			return false;
		
		String varName = command.substring(0, endPos).trim();
		command = command.substring(endPos);
		
		//now check for the assignment operator
		Matcher assignmentMatcher = _assignmentOp.matcher(command);
		if (!assignmentMatcher.lookingAt())
			return false;
		
		String expression = command.substring(assignmentMatcher.end()).trim();
		
		//calculate the expression value
		double expVal = _expressionSolver.calculateExpression(expression);
		assignValue(varName, expVal);
		
		OutputFormatter.printOutput(_expressionSolver.getVarValue(varName));
		
		return true;
	}
	
	/**
	 * This abstract function takes a variable name and the expression value and updates the
	 * variable value accordingly. This is the only operation that the subclasses implement
	 * differently.
	 * @param varName the variable to update.
	 * @param expVal the expression value to use.
	 * @throws UnrecognizedVariable if the specified variable was unrecognized.
	 */
	protected abstract void assignValue (String varName, double expVal) throws UnrecognizedVariable;

}
